/*!
 * \file TraderCTP.cpp
 * \project	WonderTrader
 *
 * \author Wesley
 * \date 2020/03/30
 *
 * \brief
 */
#include "TraderCTPMini.h"

#include "../Includes/IBaseDataMgr.h"
#include "../Includes/WTSContractInfo.hpp"
#include "../Includes/WTSDataDef.hpp"
#include "../Includes/WTSError.hpp"
#include "../Includes/WTSSessionInfo.hpp"
#include "../Includes/WTSTradeDef.hpp"
#include "../Includes/WTSVariant.hpp"

#include "../Share/ModuleHelper.hpp"
#include "../Share/decimal.h"

#include <boost/filesystem.hpp>

const char* ENTRUST_SECTION = "entrusts";
const char* ORDER_SECTION = "orders";

// By Wesley @ 2022.01.05
#include "../Share/fmtlib.h"
template <typename... Args>
inline void write_log(ITraderSpi* sink, WTSLogLevel ll, const char* format,
                      const Args&... args) {
    if (sink == NULL)
        return;

    const char* buffer = fmtutil::format(format, args...);

    sink->handleTraderLog(ll, buffer);
}

uint32_t strToTime(const char* strTime) {
    std::string str;
    const char* pos = strTime;
    while (strlen(pos) > 0) {
        if (pos[0] != ':') {
            str.append(pos, 1);
        }
        pos++;
    }

    return strtoul(str.c_str(), NULL, 10);
}

extern "C" {
EXPORT_FLAG ITraderApi* createTrader() {
    TraderCTPMini* instance = new TraderCTPMini();
    return instance;
}

EXPORT_FLAG void deleteTrader(ITraderApi*& trader) {
    if (NULL != trader) {
        delete trader;
        trader = NULL;
    }
}
}

TraderCTPMini::TraderCTPMini()
    : m_pUserAPI(NULL), m_mapPosition(NULL), m_ayOrders(NULL), m_ayTrades(NULL),
      m_ayPosDetail(NULL), m_ayFunds(NULL), m_wrapperState(WS_NOTLOGIN),
      m_uLastQryTime(0), m_iRequestID(0), m_bQuickStart(false)
      //, m_hInstCTP(NULL)
      ,
      m_bInQuery(false), m_bStopped(false), m_lastQryTime(0) {}

TraderCTPMini::~TraderCTPMini() {}

bool TraderCTPMini::init(WTSVariant* params) {
    m_strFront = params->get("front")->asCString();
    m_strBroker = params->get("broker")->asCString();
    m_strUser = params->get("user")->asCString();
    m_strPass = params->get("pass")->asCString();

    m_strAppID = params->getCString("appid");
    m_strAuthCode = params->getCString("authcode");

    m_strFlowDir = params->getCString("flowdir");
    if (m_strFlowDir.empty())
        m_strFlowDir = "CTPMiniTDFlow";

    m_strFlowDir = StrUtil::standardisePath(m_strFlowDir);

    std::string module = params->getCString("ctpmodule");
    if (module.empty())
        module = "thosttraderapi";

    m_strModule = getBinDir() + DLLHelper::wrap_module(module.c_str(), "");

    m_hInstCTP = DLLHelper::load_library(m_strModule.c_str());
#ifdef _WIN32
#ifdef _WIN64
    const char* creatorName =
        "?CreateFtdcTraderApi@CThostFtdcTraderApi@@SAPEAV1@PEBD@Z";
#else
    const char* creatorName =
        "?CreateFtdcTraderApi@CThostFtdcTraderApi@@SAPAV1@PBD@Z";
#endif
#else
    const char* creatorName =
        "_ZN19CThostFtdcTraderApi19CreateFtdcTraderApiEPKc";
#endif
    m_funcCreator = (CTPCreator)DLLHelper::get_symbol(m_hInstCTP, creatorName);

    m_bQuickStart = params->getBoolean("quick");

    return true;
}

void TraderCTPMini::release() {
    if (m_pUserAPI) {
        // m_pUserAPI->RegisterSpi(NULL);
        m_pUserAPI->Release();
        m_pUserAPI = NULL;
    }

    if (m_ayOrders)
        m_ayOrders->clear();

    if (m_ayPosDetail)
        m_ayPosDetail->clear();

    if (m_mapPosition)
        m_mapPosition->clear();

    if (m_ayTrades)
        m_ayTrades->clear();

    if (m_ayFunds)
        m_ayFunds->clear();
}

void TraderCTPMini::connect() {
    std::stringstream ss;
    ss << m_strFlowDir << "flows/" << m_strBroker << "/" << m_strUser << "/";
    boost::filesystem::create_directories(ss.str().c_str());
    m_pUserAPI = m_funcCreator(ss.str().c_str());
    m_pUserAPI->RegisterSpi(this);
    if (m_bQuickStart) {
        m_pUserAPI->SubscribePublicTopic(THOST_TERT_QUICK);  // 注册公有流
        m_pUserAPI->SubscribePrivateTopic(THOST_TERT_QUICK); // 注册私有流
    }
    else {
        m_pUserAPI->SubscribePublicTopic(THOST_TERT_RESUME);  // 注册公有流
        m_pUserAPI->SubscribePrivateTopic(THOST_TERT_RESUME); // 注册私有流
    }

    m_pUserAPI->RegisterFront((char*)m_strFront.c_str());

    if (m_pUserAPI) {
        m_pUserAPI->Init();
    }

    if (m_thrdWorker == NULL) {
        m_thrdWorker.reset(new StdThread([this]() {
            while (!m_bStopped) {
                if (m_queQuery.empty() || m_bInQuery) {
                    std::this_thread::sleep_for(std::chrono::milliseconds(1));
                    continue;
                }

                uint64_t curTime = TimeUtils::getLocalTimeNow();
                if (curTime - m_lastQryTime < 1000) {
                    std::this_thread::sleep_for(std::chrono::milliseconds(50));
                    continue;
                }

                m_bInQuery = true;
                CommonExecuter& handler = m_queQuery.front();
                handler();

                {
                    StdUniqueLock lock(m_mtxQuery);
                    m_queQuery.pop();
                }

                m_lastQryTime = TimeUtils::getLocalTimeNow();
            }
        }));
    }
}

void TraderCTPMini::disconnect() {
    m_queQuery.push([this]() { release(); });

    if (m_thrdWorker) {
        m_thrdWorker->join();
        m_thrdWorker = NULL;
    }
}

bool TraderCTPMini::makeEntrustID(char* buffer, int length) {
    if (buffer == NULL || length == 0)
        return false;

    try {
        memset(buffer, 0, length);
        uint32_t orderref = m_orderRef.fetch_add(1) + 1;
        fmt::format_to(buffer, "{:06d}#{:010d}#{:06d}", m_frontID,
                       (uint32_t)m_sessionID, orderref);
        return true;
    } catch (...) {
    }

    return false;
}

void TraderCTPMini::registerSpi(ITraderSpi* listener) {
    m_sink = listener;
    if (m_sink) {
        m_bdMgr = listener->getBaseDataMgr();
    }
}

uint32_t TraderCTPMini::genRequestID() { return m_iRequestID.fetch_add(1) + 1; }

int TraderCTPMini::login(const char* user, const char* pass,
                         const char* productInfo) {
    m_strUser = user;
    m_strPass = pass;
    m_strProdInfo = productInfo;

    if (m_pUserAPI == NULL) {
        return -1;
    }

    m_wrapperState = WS_LOGINING;
    authenticate();

    return 0;
}

int TraderCTPMini::doLogin() {
    CThostFtdcReqUserLoginField req;
    memset(&req, 0, sizeof(req));
    strcpy(req.BrokerID, m_strBroker.c_str());
    strcpy(req.UserID, m_strUser.c_str());
    strcpy(req.Password, m_strPass.c_str());
    strcpy(req.UserProductInfo, m_strProdInfo.c_str());
    int iResult = m_pUserAPI->ReqUserLogin(&req, genRequestID());
    if (iResult != 0) {
        write_log(m_sink, LL_ERROR,
                  "[TraderCTPMini] Sending login request failed: {}", iResult);
    }

    return 0;
}

int TraderCTPMini::logout() {
    if (m_pUserAPI == NULL) {
        return -1;
    }

    CThostFtdcUserLogoutField req;
    memset(&req, 0, sizeof(req));
    strcpy(req.BrokerID, m_strBroker.c_str());
    strcpy(req.UserID, m_strUser.c_str());
    int iResult = m_pUserAPI->ReqUserLogout(&req, genRequestID());
    if (iResult != 0) {
        write_log(m_sink, LL_ERROR,
                  "[TraderCTPMini] Sending logout request failed: {}", iResult);
    }

    return 0;
}

int TraderCTPMini::orderInsert(WTSEntrust* entrust) {
    if (m_pUserAPI == NULL || m_wrapperState != WS_ALLREADY) {
        return -1;
    }

    CThostFtdcInputOrderField req;
    memset(&req, 0, sizeof(req));
    ///经纪公司代码
    strcpy(req.BrokerID, m_strBroker.c_str());
    ///投资者代码
    strcpy(req.InvestorID, m_strUser.c_str());
    ///合约代码
    strcpy(req.InstrumentID, entrust->getCode());

    strcpy(req.ExchangeID, entrust->getExchg());

    if (strlen(entrust->getUserTag()) == 0) {
        ///报单引用
        fmt::format_to(req.OrderRef, "{}", m_orderRef.fetch_add(0));
    }
    else {
        uint32_t fid, sid, orderref;
        extractEntrustID(entrust->getEntrustID(), fid, sid, orderref);
        ///报单引用
        fmt::format_to(req.OrderRef, "{}", orderref);
    }

    if (strlen(entrust->getUserTag()) > 0) {
        m_eidCache.put(entrust->getEntrustID(), entrust->getUserTag(), 0,
                       [this](const char* message) {
                           write_log(m_sink, LL_WARN, message);
                       });
    }

    WTSContractInfo* ct = entrust->getContractInfo();
    if (ct == NULL)
        return -1;

    WTSCommodityInfo* commInfo = ct->getCommInfo();

    ///用户代码
    //	TThostFtdcUserIDType	UserID;
    ///报单价格条件: 限价
    req.OrderPriceType = wrapPriceType(
        entrust->getPriceType(), strcmp(commInfo->getExchg(), "CFFEX") == 0);
    ///买卖方向:
    req.Direction =
        wrapDirectionType(entrust->getDirection(), entrust->getOffsetType());
    ///组合开平标志: 开仓
    req.CombOffsetFlag[0] = wrapOffsetType(entrust->getOffsetType());
    ///组合投机套保标志
    req.CombHedgeFlag[0] = THOST_FTDC_HF_Speculation;
    ///价格
    req.LimitPrice = entrust->getPrice();
    ///数量: 1
    req.VolumeTotalOriginal = (int)entrust->getVolume();

    if (entrust->getOrderFlag() == WOF_NOR) {
        req.TimeCondition = THOST_FTDC_TC_GFD;
        req.VolumeCondition = THOST_FTDC_VC_AV;
    }
    else if (entrust->getOrderFlag() == WOF_FAK) {
        req.TimeCondition = THOST_FTDC_TC_IOC;
        req.VolumeCondition = THOST_FTDC_VC_AV;
    }
    else if (entrust->getOrderFlag() == WOF_FOK) {
        req.TimeCondition = THOST_FTDC_TC_IOC;
        req.VolumeCondition = THOST_FTDC_VC_CV;
    }

    ///触发条件: 立即
    req.ContingentCondition = THOST_FTDC_CC_Immediately;
    ///止损价
    //	TThostFtdcPriceType	StopPrice;
    ///强平原因: 非强平
    req.ForceCloseReason = THOST_FTDC_FCC_NotForceClose;
    ///自动挂起标志: 否
    req.IsAutoSuspend = 0;
    ///业务单元
    //	TThostFtdcBusinessUnitType	BusinessUnit;
    ///请求编号
    //	TThostFtdcRequestIDType	RequestID;
    ///用户强评标志: 否
    req.UserForceClose = 0;

    int iResult = m_pUserAPI->ReqOrderInsert(&req, genRequestID());
    if (iResult != 0) {
        write_log(m_sink, LL_ERROR,
                  "[TraderCTPMini] Order inserting failed: {}", iResult);
    }

    return 0;
}

int TraderCTPMini::orderAction(WTSEntrustAction* action) {
    if (m_wrapperState != WS_ALLREADY)
        return -1;

    uint32_t frontid, sessionid, orderref;
    if (!extractEntrustID(action->getEntrustID(), frontid, sessionid, orderref))
        return -1;

    CThostFtdcInputOrderActionField req;
    memset(&req, 0, sizeof(req));
    ///经纪公司代码
    strcpy(req.BrokerID, m_strBroker.c_str());
    ///投资者代码
    strcpy(req.InvestorID, m_strUser.c_str());
    ///报单引用
    fmt::format_to(req.OrderRef, "{}", orderref);
    ///请求编号
    ///前置编号
    req.FrontID = frontid;
    ///会话编号
    req.SessionID = sessionid;
    ///操作标志
    req.ActionFlag = wrapActionFlag(action->getActionFlag());
    ///合约代码
    strcpy(req.InstrumentID, action->getCode());

    req.LimitPrice = action->getPrice();

    req.VolumeChange = (int32_t)action->getVolume();

    strcpy(req.OrderSysID, action->getOrderID());
    strcpy(req.ExchangeID, action->getExchg());

    int iResult = m_pUserAPI->ReqOrderAction(&req, genRequestID());
    if (iResult != 0) {
        write_log(m_sink, LL_ERROR,
                  "[TraderCTPMini] Sending cancel request failed: {}", iResult);
    }

    return 0;
}

int TraderCTPMini::queryAccount() {
    if (m_pUserAPI == NULL || m_wrapperState != WS_ALLREADY) {
        return -1;
    }

    {
        StdUniqueLock lock(m_mtxQuery);
        m_queQuery.push([this]() {
            CThostFtdcQryTradingAccountField req;
            memset(&req, 0, sizeof(req));
            strcpy(req.BrokerID, m_strBroker.c_str());
            strcpy(req.InvestorID, m_strUser.c_str());
            m_pUserAPI->ReqQryTradingAccount(&req, genRequestID());
        });
    }

    // triggerQuery();

    return 0;
}

int TraderCTPMini::queryPositions() {
    if (m_pUserAPI == NULL || m_wrapperState != WS_ALLREADY) {
        return -1;
    }

    {
        StdUniqueLock lock(m_mtxQuery);
        m_queQuery.push([this]() {
            CThostFtdcQryInvestorPositionField req;
            memset(&req, 0, sizeof(req));
            strcpy(req.BrokerID, m_strBroker.c_str());
            strcpy(req.InvestorID, m_strUser.c_str());
            m_pUserAPI->ReqQryInvestorPosition(&req, genRequestID());
        });
    }

    // triggerQuery();

    return 0;
}

int TraderCTPMini::queryOrders() {
    if (m_pUserAPI == NULL || m_wrapperState != WS_ALLREADY) {
        return -1;
    }
    {
        StdUniqueLock lock(m_mtxQuery);
        m_queQuery.push([this]() {
            CThostFtdcQryOrderField req;
            memset(&req, 0, sizeof(req));
            strcpy(req.BrokerID, m_strBroker.c_str());
            strcpy(req.InvestorID, m_strUser.c_str());

            m_pUserAPI->ReqQryOrder(&req, genRequestID());
        });
    }

    // triggerQuery();

    return 0;
}

int TraderCTPMini::queryTrades() {
    if (m_pUserAPI == NULL || m_wrapperState != WS_ALLREADY) {
        return -1;
    }

    {
        StdUniqueLock lock(m_mtxQuery);
        m_queQuery.push([this]() {
            CThostFtdcQryTradeField req;
            memset(&req, 0, sizeof(req));
            strcpy(req.BrokerID, m_strBroker.c_str());
            strcpy(req.InvestorID, m_strUser.c_str());

            m_pUserAPI->ReqQryTrade(&req, genRequestID());
        });
    }

    // triggerQuery();

    return 0;
}

void TraderCTPMini::OnFrontConnected() {
    if (m_sink)
        m_sink->handleEvent(WTE_Connect, 0);
}

void TraderCTPMini::OnFrontDisconnected(int nReason) {
    // write_log(m_sink, LL_ERROR, "[TraderCTPMini]CTP交易服务器已断开");
    m_wrapperState = WS_NOTLOGIN;
    if (m_sink)
        m_sink->handleEvent(WTE_Close, nReason);
}

void TraderCTPMini::OnHeartBeatWarning(int nTimeLapse) {
    write_log(m_sink, LL_DEBUG, "[TraderCTPMini][{}-{}] Heartbeating...",
              m_strBroker.c_str(), m_strUser.c_str());
}

void TraderCTPMini::OnRspAuthenticate(
    CThostFtdcRspAuthenticateField* pRspAuthenticateField,
    CThostFtdcRspInfoField* pRspInfo, int nRequestID, bool bIsLast) {
    if (!IsErrorRspInfo(pRspInfo)) {
        doLogin();
    }
    else {
        write_log(m_sink, LL_ERROR,
                  "[TraderCTPMini][{}-{}] Authentication failed: {}",
                  m_strBroker.c_str(), m_strUser.c_str(), pRspInfo->ErrorMsg);
        m_wrapperState = WS_LOGINFAILED;

        if (m_sink)
            m_sink->onLoginResult(false, pRspInfo->ErrorMsg, 0);
    }
}

void TraderCTPMini::OnRspUserLogin(CThostFtdcRspUserLoginField* pRspUserLogin,
                                   CThostFtdcRspInfoField* pRspInfo,
                                   int nRequestID, bool bIsLast) {
    if (!IsErrorRspInfo(pRspInfo)) {
        m_wrapperState = WS_LOGINED;

        // 保存会话参数
        m_frontID = pRspUserLogin->FrontID;
        m_sessionID = pRspUserLogin->SessionID;
        m_orderRef = atoi(pRspUserLogin->MaxOrderRef);
        ///获取当前交易日
        m_lDate = atoi(m_pUserAPI->GetTradingDay());

        write_log(m_sink, LL_INFO,
                  "[TraderCTPMini][{}-{}] Login succeed, AppID: {}, Sessionid: "
                  "{}, login time: {}...",
                  m_strBroker.c_str(), m_strUser.c_str(), m_strAppID.c_str(),
                  m_sessionID, pRspUserLogin->LoginTime);

        {
            //初始化委托单缓存器
            std::stringstream ss;
            ss << m_strFlowDir << "local/" << m_strBroker << "/";
            std::string path = StrUtil::standardisePath(ss.str());
            if (!StdFile::exists(path.c_str()))
                boost::filesystem::create_directories(path.c_str());
            ss << m_strUser << "_eid.sc";
            m_eidCache.init(ss.str().c_str(), m_lDate,
                            [this](const char* message) {
                                write_log(m_sink, LL_WARN, message);
                            });
        }

        {
            //初始化订单标记缓存器
            std::stringstream ss;
            ss << m_strFlowDir << "local/" << m_strBroker << "/";
            std::string path = StrUtil::standardisePath(ss.str());
            if (!StdFile::exists(path.c_str()))
                boost::filesystem::create_directories(path.c_str());
            ss << m_strUser << "_oid.sc";
            m_oidCache.init(ss.str().c_str(), m_lDate,
                            [this](const char* message) {
                                write_log(m_sink, LL_WARN, message);
                            });
        }

        write_log(m_sink, LL_INFO,
                  "[TraderCTPMini][{}-{}] Login succeed, trading date: {}...",
                  m_strBroker.c_str(), m_strUser.c_str(), m_lDate);

        write_log(m_sink, LL_INFO,
                  "[TraderCTPMini][{}-{}] Trading channel initialized...",
                  m_strBroker.c_str(), m_strUser.c_str());
        m_wrapperState = WS_ALLREADY;
        if (m_sink)
            m_sink->onLoginResult(true, "", m_lDate);
    }
    else {
        write_log(m_sink, LL_ERROR, "[TraderCTPMini][{}-{}] Login failed: {}",
                  m_strBroker.c_str(), m_strUser.c_str(), pRspInfo->ErrorMsg);
        m_wrapperState = WS_LOGINFAILED;

        if (m_sink)
            m_sink->onLoginResult(false, pRspInfo->ErrorMsg, 0);
    }
}

void TraderCTPMini::OnRspUserLogout(CThostFtdcUserLogoutField* pUserLogout,
                                    CThostFtdcRspInfoField* pRspInfo,
                                    int nRequestID, bool bIsLast) {
    m_wrapperState = WS_NOTLOGIN;
    if (m_sink)
        m_sink->handleEvent(WTE_Logout, 0);
}

void TraderCTPMini::OnRspOrderInsert(CThostFtdcInputOrderField* pInputOrder,
                                     CThostFtdcRspInfoField* pRspInfo,
                                     int nRequestID, bool bIsLast) {
    WTSEntrust* entrust = makeEntrust(pInputOrder);
    if (entrust) {
        WTSError* err = makeError(pRspInfo);
        // g_orderMgr.onRspEntrust(entrust, err);
        if (m_sink)
            m_sink->onRspEntrust(entrust, err);
        entrust->release();
        err->release();
    }
}

void TraderCTPMini::OnRspOrderAction(
    CThostFtdcInputOrderActionField* pInputOrderAction,
    CThostFtdcRspInfoField* pRspInfo, int nRequestID, bool bIsLast) {
    if (IsErrorRspInfo(pRspInfo)) {
    }
    else {
        WTSError* error = WTSError::create(WEC_ORDERCANCEL, pRspInfo->ErrorMsg);
        if (m_sink)
            m_sink->onTraderError(error);
    }
}

void TraderCTPMini::OnRspQryTradingAccount(
    CThostFtdcTradingAccountField* pTradingAccount,
    CThostFtdcRspInfoField* pRspInfo, int nRequestID, bool bIsLast) {
    if (bIsLast) {
        m_bInQuery = false;
        // triggerQuery();
    }

    if (!IsErrorRspInfo(pRspInfo) && pTradingAccount) {
        if (NULL == m_ayFunds)
            m_ayFunds = WTSArray::create();

        WTSAccountInfo* accountInfo = WTSAccountInfo::create();
        accountInfo->setPreBalance(pTradingAccount->PreBalance);
        accountInfo->setCloseProfit(pTradingAccount->CloseProfit);
        accountInfo->setDynProfit(pTradingAccount->PositionProfit);
        accountInfo->setMargin(pTradingAccount->CurrMargin);
        accountInfo->setAvailable(pTradingAccount->Available);
        accountInfo->setCommission(pTradingAccount->Commission);
        accountInfo->setFrozenMargin(pTradingAccount->FrozenMargin);
        accountInfo->setFrozenCommission(pTradingAccount->FrozenCommission);
        accountInfo->setDeposit(pTradingAccount->Deposit);
        accountInfo->setWithdraw(pTradingAccount->Withdraw);
        accountInfo->setBalance(
            accountInfo->getPreBalance() + accountInfo->getCloseProfit() -
            accountInfo->getCommission() + accountInfo->getDeposit() -
            accountInfo->getWithdraw());

        accountInfo->setCurrency("CNY");
        m_ayFunds->append(accountInfo, false);
    }

    if (bIsLast) {
        if (m_sink)
            m_sink->onRspAccount(m_ayFunds);

        m_ayFunds->clear();
    }
}

void TraderCTPMini::OnRspQryInvestorPosition(
    CThostFtdcInvestorPositionField* pInvestorPosition,
    CThostFtdcRspInfoField* pRspInfo, int nRequestID, bool bIsLast) {
    if (bIsLast) {
        m_bInQuery = false;
        // triggerQuery();
    }

    if (!IsErrorRspInfo(pRspInfo) && pInvestorPosition) {
        if (NULL == m_mapPosition)
            m_mapPosition = PositionMap::create();

        WTSContractInfo* contract =
            m_bdMgr->getContract(pInvestorPosition->InstrumentID);
        if (contract) {
            WTSCommodityInfo* commInfo = contract->getCommInfo();
            std::string key =
                fmt::format("{}-{}", pInvestorPosition->InstrumentID,
                            pInvestorPosition->PosiDirection);
            WTSPositionItem* pos = (WTSPositionItem*)m_mapPosition->get(key);
            if (pos == NULL) {
                pos = WTSPositionItem::create(pInvestorPosition->InstrumentID,
                                              commInfo->getCurrency(),
                                              commInfo->getExchg());
                pos->setContractInfo(contract);
                m_mapPosition->add(key, pos, false);
            }
            pos->setDirection(
                wrapPosDirection(pInvestorPosition->PosiDirection));
            if (commInfo->getCoverMode() == CM_CoverToday) {
                if (pInvestorPosition->PositionDate == THOST_FTDC_PSD_Today)
                    pos->setNewPosition(pInvestorPosition->Position);
                else
                    pos->setPrePosition(pInvestorPosition->Position);
            }
            else {
                pos->setNewPosition(pInvestorPosition->TodayPosition);
                pos->setPrePosition(pInvestorPosition->Position -
                                    pInvestorPosition->TodayPosition);
            }

            pos->setMargin(pos->getMargin() + pInvestorPosition->UseMargin);
            pos->setDynProfit(pos->getDynProfit() +
                              pInvestorPosition->PositionProfit);
            pos->setPositionCost(pos->getPositionCost() +
                                 pInvestorPosition->PositionCost);

            if (pos->getTotalPosition() != 0) {
                pos->setAvgPrice(pos->getPositionCost() /
                                 pos->getTotalPosition() /
                                 commInfo->getVolScale());
            }
            else {
                pos->setAvgPrice(0);
            }

            if (commInfo->getCategoty() != CC_Combination) {
                if (commInfo->getCoverMode() == CM_CoverToday) {
                    if (pInvestorPosition->PositionDate ==
                        THOST_FTDC_PSD_Today) {
                        int availNew = pInvestorPosition->Position;
                        if (pInvestorPosition->PosiDirection ==
                            THOST_FTDC_PD_Long) {
                            availNew -= pInvestorPosition->ShortFrozen;
                        }
                        else {
                            availNew -= pInvestorPosition->LongFrozen;
                        }
                        if (availNew < 0)
                            availNew = 0;
                        pos->setAvailNewPos(availNew);
                    }
                    else {
                        int availPre = pInvestorPosition->Position;
                        if (pInvestorPosition->PosiDirection ==
                            THOST_FTDC_PD_Long) {
                            availPre -= pInvestorPosition->ShortFrozen;
                        }
                        else {
                            availPre -= pInvestorPosition->LongFrozen;
                        }
                        if (availPre < 0)
                            availPre = 0;
                        pos->setAvailPrePos(availPre);
                    }
                }
                else {
                    int availNew = pInvestorPosition->TodayPosition;
                    if (pInvestorPosition->PosiDirection ==
                        THOST_FTDC_PD_Long) {
                        availNew -= pInvestorPosition->ShortFrozen;
                    }
                    else {
                        availNew -= pInvestorPosition->LongFrozen;
                    }
                    if (availNew < 0)
                        availNew = 0;
                    pos->setAvailNewPos(availNew);

                    double availPre =
                        pos->getNewPosition() + pos->getPrePosition() -
                        pInvestorPosition->LongFrozen -
                        pInvestorPosition->ShortFrozen - pos->getAvailNewPos();
                    pos->setAvailPrePos(availPre);
                }
            }
            else {
            }

            if (decimal::lt(pos->getTotalPosition(), 0.0) &&
                decimal::eq(pos->getMargin(), 0.0)) {
                //有仓位,但是保证金为0,则说明是套利合约,单个合约的可用持仓全部置为0
                pos->setAvailNewPos(0);
                pos->setAvailPrePos(0);
            }
        }
    }

    if (bIsLast) {

        WTSArray* ayPos = WTSArray::create();

        if (m_mapPosition && m_mapPosition->size() > 0) {
            for (auto it = m_mapPosition->begin(); it != m_mapPosition->end();
                 it++) {
                ayPos->append(it->second, true);
            }
        }

        if (m_sink)
            m_sink->onRspPosition(ayPos);

        if (m_mapPosition) {
            m_mapPosition->release();
            m_mapPosition = NULL;
        }

        ayPos->release();
    }
}

void TraderCTPMini::OnRspQryTrade(CThostFtdcTradeField* pTrade,
                                  CThostFtdcRspInfoField* pRspInfo,
                                  int nRequestID, bool bIsLast) {
    if (bIsLast) {
        m_bInQuery = false;
        // triggerQuery();
    }

    if (!IsErrorRspInfo(pRspInfo) && pTrade) {
        if (NULL == m_ayTrades)
            m_ayTrades = WTSArray::create();

        WTSTradeInfo* trade = makeTradeRecord(pTrade);
        if (trade) {
            m_ayTrades->append(trade, false);
        }
    }

    if (bIsLast) {
        if (m_sink)
            m_sink->onRspTrades(m_ayTrades);

        if (NULL != m_ayTrades)
            m_ayTrades->clear();
    }
}

void TraderCTPMini::OnRspQryOrder(CThostFtdcOrderField* pOrder,
                                  CThostFtdcRspInfoField* pRspInfo,
                                  int nRequestID, bool bIsLast) {
    if (bIsLast) {
        m_bInQuery = false;
        // triggerQuery();
    }

    if (!IsErrorRspInfo(pRspInfo) && pOrder) {
        if (NULL == m_ayOrders)
            m_ayOrders = WTSArray::create();

        WTSOrderInfo* orderInfo = makeOrderInfo(pOrder);
        if (orderInfo) {
            m_ayOrders->append(orderInfo, false);
        }
    }

    if (bIsLast) {
        if (m_sink)
            m_sink->onRspOrders(m_ayOrders);

        if (m_ayOrders)
            m_ayOrders->clear();
    }
}

void TraderCTPMini::OnRspError(CThostFtdcRspInfoField* pRspInfo, int nRequestID,
                               bool bIsLast) {
    int x = 0;
}

void TraderCTPMini::OnRtnOrder(CThostFtdcOrderField* pOrder) {
    WTSOrderInfo* orderInfo = makeOrderInfo(pOrder);
    if (orderInfo) {
        if (m_sink)
            m_sink->onPushOrder(orderInfo);

        orderInfo->release();
    }

    // ReqQryTradingAccount();
}

void TraderCTPMini::OnRtnTrade(CThostFtdcTradeField* pTrade) {
    WTSTradeInfo* tRecord = makeTradeRecord(pTrade);
    if (tRecord) {
        if (m_sink)
            m_sink->onPushTrade(tRecord);

        tRecord->release();
    }
}

int TraderCTPMini::wrapDirectionType(WTSDirectionType dirType,
                                     WTSOffsetType offsetType) {
    if (WDT_LONG == dirType)
        if (offsetType == WOT_OPEN)
            return THOST_FTDC_D_Buy;
        else
            return THOST_FTDC_D_Sell;
    else if (offsetType == WOT_OPEN)
        return THOST_FTDC_D_Sell;
    else
        return THOST_FTDC_D_Buy;
}

WTSDirectionType
TraderCTPMini::wrapDirectionType(TThostFtdcDirectionType dirType,
                                 TThostFtdcOffsetFlagType offsetType) {
    if (THOST_FTDC_D_Buy == dirType)
        if (offsetType == THOST_FTDC_OF_Open)
            return WDT_LONG;
        else
            return WDT_SHORT;
    else if (offsetType == THOST_FTDC_OF_Open)
        return WDT_SHORT;
    else
        return WDT_LONG;
}

WTSDirectionType
TraderCTPMini::wrapPosDirection(TThostFtdcPosiDirectionType dirType) {
    if (THOST_FTDC_PD_Long == dirType)
        return WDT_LONG;
    else
        return WDT_SHORT;
}

int TraderCTPMini::wrapOffsetType(WTSOffsetType offType) {
    if (WOT_OPEN == offType)
        return THOST_FTDC_OF_Open;
    else if (WOT_CLOSE == offType)
        return THOST_FTDC_OF_Close;
    else if (WOT_CLOSETODAY == offType)
        return THOST_FTDC_OF_CloseToday;
    else if (WOT_CLOSEYESTERDAY == offType)
        return THOST_FTDC_OF_Close;
    else
        return THOST_FTDC_OF_ForceClose;
}

WTSOffsetType TraderCTPMini::wrapOffsetType(TThostFtdcOffsetFlagType offType) {
    if (THOST_FTDC_OF_Open == offType)
        return WOT_OPEN;
    else if (THOST_FTDC_OF_Close == offType)
        return WOT_CLOSE;
    else if (THOST_FTDC_OF_CloseToday == offType)
        return WOT_CLOSETODAY;
    else
        return WOT_FORCECLOSE;
}

int TraderCTPMini::wrapPriceType(WTSPriceType priceType,
                                 bool isCFFEX /* = false */) {
    if (WPT_ANYPRICE == priceType)
        return isCFFEX ? THOST_FTDC_OPT_FiveLevelPrice
                       : THOST_FTDC_OPT_AnyPrice;
    else if (WPT_LIMITPRICE == priceType)
        return THOST_FTDC_OPT_LimitPrice;
    else if (WPT_BESTPRICE == priceType)
        return THOST_FTDC_OPT_BestPrice;
    else
        return THOST_FTDC_OPT_LastPrice;
}

WTSPriceType
TraderCTPMini::wrapPriceType(TThostFtdcOrderPriceTypeType priceType) {
    if (THOST_FTDC_OPT_AnyPrice == priceType ||
        THOST_FTDC_OPT_FiveLevelPrice == priceType)
        return WPT_ANYPRICE;
    else if (THOST_FTDC_OPT_LimitPrice == priceType)
        return WPT_LIMITPRICE;
    else if (THOST_FTDC_OPT_BestPrice == priceType)
        return WPT_BESTPRICE;
    else
        return WPT_LASTPRICE;
}

int TraderCTPMini::wrapTimeCondition(WTSTimeCondition timeCond) {
    if (WTC_IOC == timeCond)
        return THOST_FTDC_TC_IOC;
    else if (WTC_GFD == timeCond)
        return THOST_FTDC_TC_GFD;
    else
        return THOST_FTDC_TC_GFS;
}

WTSTimeCondition
TraderCTPMini::wrapTimeCondition(TThostFtdcTimeConditionType timeCond) {
    if (THOST_FTDC_TC_IOC == timeCond)
        return WTC_IOC;
    else if (THOST_FTDC_TC_GFD == timeCond)
        return WTC_GFD;
    else
        return WTC_GFS;
}

WTSOrderState
TraderCTPMini::wrapOrderState(TThostFtdcOrderStatusType orderState) {
    if (orderState != THOST_FTDC_OST_Unknown)
        return (WTSOrderState)orderState;
    else
        return WOS_Submitting;
}

int TraderCTPMini::wrapActionFlag(WTSActionFlag actionFlag) {
    if (WAF_CANCEL == actionFlag)
        return THOST_FTDC_AF_Delete;
    else
        return THOST_FTDC_AF_Modify;
}

WTSOrderInfo* TraderCTPMini::makeOrderInfo(CThostFtdcOrderField* orderField) {
    WTSContractInfo* contract = m_bdMgr->getContract(orderField->InstrumentID);
    if (contract == NULL)
        return NULL;

    WTSOrderInfo* pRet = WTSOrderInfo::create();
    pRet->setContractInfo(contract);
    pRet->setPrice(orderField->LimitPrice);
    pRet->setVolume(orderField->VolumeTotalOriginal);
    pRet->setDirection(wrapDirectionType(orderField->Direction,
                                         orderField->CombOffsetFlag[0]));
    pRet->setPriceType(wrapPriceType(orderField->OrderPriceType));
    pRet->setOffsetType(wrapOffsetType(orderField->CombOffsetFlag[0]));
    if (orderField->TimeCondition == THOST_FTDC_TC_GFD) {
        pRet->setOrderFlag(WOF_NOR);
    }
    else if (orderField->TimeCondition == THOST_FTDC_TC_IOC) {
        if (orderField->VolumeCondition == THOST_FTDC_VC_AV ||
            orderField->VolumeCondition == THOST_FTDC_VC_MV)
            pRet->setOrderFlag(WOF_FAK);
        else
            pRet->setOrderFlag(WOF_FOK);
    }

    pRet->setVolTraded(orderField->VolumeTraded);
    pRet->setVolLeft(orderField->VolumeTotal);

    pRet->setCode(orderField->InstrumentID);
    pRet->setExchange(contract->getExchg());

    /*
     *	By Wesley @ 2021.12.16
     *	mini返回的InsertDate是空，所以这里要改成用交易日来重算，但是夜盘很麻烦，只能大概处理一下了
     */
    std::string strTime = orderField->InsertTime;
    StrUtil::replace(strTime, ":", "");
    uint32_t uTime = strtoul(strTime.c_str(), NULL, 10);

    uint32_t insertDate = m_lDate;
    uint32_t uHour = uTime / 10000;
    //下单时间是夜盘，则直接用交易日倒推下单日期
    if (uHour >= 21) {
        uint32_t wd = TimeUtils::getWeekDay(insertDate);
        //如果是周一，则直接用上周五的日期
        //因为节假日前一天不会有夜盘，所以只可能是周五
        //如果不是周一，则直接用前一天的日期
        if (wd == 1)
            insertDate = TimeUtils::getNextDate(m_lDate, -3);
        else
            insertDate = TimeUtils::getNextDate(m_lDate, -1);
    }
    pRet->setOrderDate(insertDate);

    pRet->setOrderTime(TimeUtils::makeTime(
        pRet->getOrderDate(), strtoul(strTime.c_str(), NULL, 10) * 1000));

    pRet->setOrderState(wrapOrderState(orderField->OrderStatus));
    if (orderField->OrderSubmitStatus >= THOST_FTDC_OSS_InsertRejected)
        pRet->setError(true);

    generateEntrustID(pRet->getEntrustID(), orderField->FrontID,
                      orderField->SessionID, atoi(orderField->OrderRef));
    pRet->setOrderID(orderField->OrderSysID);

    pRet->setStateMsg(orderField->StatusMsg);

    const char* usertag = m_eidCache.get(pRet->getEntrustID());
    if (strlen(usertag) == 0) {
        pRet->setUserTag(pRet->getEntrustID());
    }
    else {
        pRet->setUserTag(usertag);

        if (strlen(pRet->getOrderID()) > 0) {
            m_oidCache.put(StrUtil::trim(pRet->getOrderID()).c_str(), usertag,
                           0, [this](const char* message) {
                               write_log(m_sink, LL_ERROR, message);
                           });
        }
    }

    return pRet;
}

WTSEntrust*
TraderCTPMini::makeEntrust(CThostFtdcInputOrderField* entrustField) {
    WTSContractInfo* ct = m_bdMgr->getContract(entrustField->InstrumentID);
    if (ct == NULL)
        return NULL;

    WTSEntrust* pRet = WTSEntrust::create(
        entrustField->InstrumentID, entrustField->VolumeTotalOriginal,
        entrustField->LimitPrice, ct->getExchg());

    pRet->setContractInfo(ct);

    pRet->setDirection(wrapDirectionType(entrustField->Direction,
                                         entrustField->CombOffsetFlag[0]));
    pRet->setPriceType(wrapPriceType(entrustField->OrderPriceType));
    pRet->setOffsetType(wrapOffsetType(entrustField->CombOffsetFlag[0]));

    if (entrustField->TimeCondition == THOST_FTDC_TC_GFD) {
        pRet->setOrderFlag(WOF_NOR);
    }
    else if (entrustField->TimeCondition == THOST_FTDC_TC_IOC) {
        if (entrustField->VolumeCondition == THOST_FTDC_VC_AV ||
            entrustField->VolumeCondition == THOST_FTDC_VC_MV)
            pRet->setOrderFlag(WOF_FAK);
        else
            pRet->setOrderFlag(WOF_FOK);
    }

    // pRet->setEntrustID(generateEntrustID(m_frontID, m_sessionID,
    // atoi(entrustField->OrderRef)).c_str());
    generateEntrustID(pRet->getEntrustID(), m_frontID, m_sessionID,
                      atoi(entrustField->OrderRef));

    const char* usertag = m_eidCache.get(pRet->getEntrustID());
    if (strlen(usertag) > 0)
        pRet->setUserTag(usertag);

    return pRet;
}

WTSError* TraderCTPMini::makeError(CThostFtdcRspInfoField* rspInfo) {
    WTSError* pRet =
        WTSError::create((WTSErroCode)rspInfo->ErrorID, rspInfo->ErrorMsg);
    return pRet;
}

WTSTradeInfo* TraderCTPMini::makeTradeRecord(CThostFtdcTradeField* tradeField) {
    WTSContractInfo* contract =
        m_bdMgr->getContract(tradeField->InstrumentID, tradeField->ExchangeID);
    if (contract == NULL)
        return NULL;

    WTSCommodityInfo* commInfo = contract->getCommInfo();

    WTSTradeInfo* pRet =
        WTSTradeInfo::create(tradeField->InstrumentID, commInfo->getExchg());
    pRet->setContractInfo(contract);
    pRet->setVolume(tradeField->Volume);
    pRet->setPrice(tradeField->Price);
    pRet->setTradeID(tradeField->TradeID);

    std::string strTime = tradeField->TradeTime;
    StrUtil::replace(strTime, ":", "");
    uint32_t uTime = strtoul(strTime.c_str(), NULL, 10);

    /*
     *	By Wesley @ 2021.12.16
     *	mini返回的InsertDate是空，所以这里要改成用交易日来重算，但是夜盘很麻烦，只能大概处理一下了
     */
    uint32_t uDate = m_lDate;
    uint32_t uHour = uTime / 10000;
    //下单时间是夜盘，则直接用交易日倒推下单日期
    if (uHour >= 21) {
        uint32_t wd = TimeUtils::getWeekDay(uDate);
        //如果是周一，则直接用上周五的日期
        //因为节假日前一天不会有夜盘，所以只可能是周五
        //如果不是周一，则直接用前一天的日期
        if (wd == 1)
            uDate = TimeUtils::getNextDate(m_lDate, -3);
        else
            uDate = TimeUtils::getNextDate(m_lDate, -1);
    }
    pRet->setTradeDate(uDate);
    pRet->setTradeTime(TimeUtils::makeTime(uDate, uTime * 1000));

    WTSDirectionType dType =
        wrapDirectionType(tradeField->Direction, tradeField->OffsetFlag);

    pRet->setDirection(dType);
    pRet->setOffsetType(wrapOffsetType(tradeField->OffsetFlag));
    pRet->setRefOrder(tradeField->OrderSysID);
    pRet->setTradeType((WTSTradeType)tradeField->TradeType);

    double amount =
        commInfo->getVolScale() * tradeField->Volume * pRet->getPrice();
    pRet->setAmount(amount);

    const char* usertag =
        m_oidCache.get(StrUtil::trim(pRet->getRefOrder()).c_str());
    if (strlen(usertag))
        pRet->setUserTag(usertag);

    return pRet;
}

void TraderCTPMini::generateEntrustID(char* buffer, uint32_t frontid,
                                      uint32_t sessionid, uint32_t orderRef) {
    fmtutil::format_to(buffer, "{:06d}#{:010d}#{:06d}", frontid, sessionid,
                       orderRef);
}

bool TraderCTPMini::extractEntrustID(const char* entrustid, uint32_t& frontid,
                                     uint32_t& sessionid, uint32_t& orderRef) {
    thread_local static char buffer[64];
    wt_strcpy(buffer, entrustid);
    char* s = buffer;
    auto idx = StrUtil::findFirst(s, '#');
    if (idx == std::string::npos)
        return false;
    s[idx] = '\0';
    frontid = strtoul(s, NULL, 10);
    s += idx + 1;

    idx = StrUtil::findFirst(s, '#');
    if (idx == std::string::npos)
        return false;
    s[idx] = '\0';
    sessionid = strtoul(s, NULL, 10);
    s += idx + 1;

    orderRef = strtoul(s, NULL, 10);

    return true;
}

bool TraderCTPMini::IsErrorRspInfo(CThostFtdcRspInfoField* pRspInfo) {
    if (pRspInfo && pRspInfo->ErrorID != 0)
        return true;

    return false;
}

void TraderCTPMini::OnErrRtnOrderInsert(CThostFtdcInputOrderField* pInputOrder,
                                        CThostFtdcRspInfoField* pRspInfo) {
    WTSEntrust* entrust = makeEntrust(pInputOrder);
    if (entrust) {
        WTSError* err = makeError(pRspInfo);
        // g_orderMgr.onRspEntrust(entrust, err);
        if (m_sink)
            m_sink->onRspEntrust(entrust, err);
        entrust->release();
        err->release();
    }
}

void TraderCTPMini::OnRtnInstrumentStatus(
    CThostFtdcInstrumentStatusField* pInstrumentStatus) {
    if (m_sink)
        m_sink->onPushInstrumentStatus(
            pInstrumentStatus->ExchangeID, pInstrumentStatus->InstrumentID,
            (WTSTradeStatus)pInstrumentStatus->InstrumentStatus);
}

bool TraderCTPMini::isConnected() { return (m_wrapperState == WS_ALLREADY); }

int TraderCTPMini::authenticate() {
    CThostFtdcReqAuthenticateField req;
    memset(&req, 0, sizeof(req));
    strcpy(req.BrokerID, m_strBroker.c_str());
    strcpy(req.UserID, m_strUser.c_str());
    // strcpy(req.UserProductInfo, m_strProdInfo.c_str());
    strcpy(req.AuthCode, m_strAuthCode.c_str());
    strcpy(req.AppID, m_strAppID.c_str());
    m_pUserAPI->ReqAuthenticate(&req, genRequestID());

    return 0;
}

/*
void TraderCTPMini::triggerQuery()
{
    m_strandIO->post([this](){
        if (m_queQuery.empty() || m_bInQuery)
            return;

        uint64_t curTime = TimeUtils::getLocalTimeNow();
        if (curTime - m_lastQryTime < 1000)
        {
            std::this_thread::sleep_for(std::chrono::milliseconds(50));
            m_strandIO->post([this](){
                triggerQuery();
            });
            return;
        }


        m_bInQuery = true;
        CommonExecuter& handler = m_queQuery.front();
        handler();

        {
            StdUniqueLock lock(m_mtxQuery);
            m_queQuery.pop();
        }

        m_lastQryTime = TimeUtils::getLocalTimeNow();
    });
}
*/